﻿using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace PhiStore.Experiments.Serialize
{
    class SerializeExperiment : IExperiment
    {
        object critical = new object();
        Boolean completed = false;
        int objCount = 0;
        
        ConstructorSerializable[] consObjs;
        GenericSerializable[] pubObjs;
        ConstructorSerializableDescriptiveNames[] descConsObjs;

        /* results */
        long consObjLength = 0L;
        long consObjSerializeTime = 0L;
        long consObjDeserializeTime = 0L;

        long descConsObjLength = 0;
        long descConsObjSerializeTime = 0L;
        long descConsObjDeserializeTime = 0L;

        long pubObjLength = 0L;
        long pubObjSerializeTime = 0L;
        long pubObjDeserializeTime = 0L;

        public SerializeExperiment(int numberOfObjects)
        {
            objCount = numberOfObjects;

            consObjs = new ConstructorSerializable[numberOfObjects];
            pubObjs = new GenericSerializable[numberOfObjects];
            descConsObjs = new ConstructorSerializableDescriptiveNames[numberOfObjects];
            for (int i = 0; i < numberOfObjects; i++)
            {
                string seed = Path.GetRandomFileName(); //random string
                consObjs[i] = new ConstructorSerializable(seed);
                pubObjs[i] = new GenericSerializable(seed);
                descConsObjs[i] = new ConstructorSerializableDescriptiveNames(seed);
            }
        }

        public string getResults()
        {
            if (!IsComplete())
            {
                return "";
            }
            else
            {
                string result = objCount + " objects serialized and deserialized\r\n";
                result += "Constructor Serializable Object Length: " + consObjLength + " bytes\r\n";
                result += "Constructor Serializable Object Serialize Time: " + consObjSerializeTime + " ms\r\n";
                result += "Constructor Serializable Object Deserialize Time: " + consObjDeserializeTime + " ms\r\n";

                result += "Descriptive Constructor Serializable Object Length: " + descConsObjLength + " bytes\r\n";
                result += "Descriptive Constructor Serializable Object Serialize Time: " + descConsObjSerializeTime + " ms\r\n";
                result += "Descriptive Constructor Serializable Object Deserialize Time: " + descConsObjDeserializeTime + " ms\r\n";

                result += "Public Serializable Object Length: " + pubObjLength + " bytes\r\n";
                result += "Public Serializable Object Serialize Time: " + pubObjSerializeTime + " ms\r\n";
                result += "Public Serializable Object Deserialize Time: " + pubObjDeserializeTime + " ms\r\n";
                
                return result;
            }
        }

        public void Run()
        {
            MemoryStream stream = new MemoryStream();
            BinaryFormatter formatter = new BinaryFormatter();

            Stopwatch watch = new Stopwatch();

            #region Constructor objects
            watch.Start();

            for (int i = 0; i < objCount; i++)
            {
                object toSerialize = consObjs[i];
                formatter.Serialize(stream, toSerialize);
            }

            watch.Stop();

            consObjSerializeTime = watch.ElapsedMilliseconds;

            consObjLength = stream.Length;
            stream.Position = 0;
            watch.Restart();

            for (int i = 0; i < objCount; i++)
            {
                ConstructorSerializable obj = (ConstructorSerializable)formatter.Deserialize(stream);
            }
            watch.Stop();
            consObjDeserializeTime = watch.ElapsedMilliseconds;
            #endregion

            #region Descriptive constructor objects
            stream.Position = 0;
            stream.SetLength(0L);
            System.GC.Collect();

            watch.Restart();
            for (int i = 0; i < objCount; i++)
            {
                object toSerialize = descConsObjs[i];
                formatter.Serialize(stream, toSerialize);
            }

            watch.Stop();

            descConsObjSerializeTime = watch.ElapsedMilliseconds;

            descConsObjLength = stream.Length;
            stream.Position = 0;
            watch.Restart();

            for (int i = 0; i < objCount; i++)
            {
                ConstructorSerializableDescriptiveNames obj = (ConstructorSerializableDescriptiveNames)formatter.Deserialize(stream);
            }
            watch.Stop();
            descConsObjDeserializeTime = watch.ElapsedMilliseconds;


            #endregion

            #region Public objects

            stream.Position = 0;
            stream.SetLength(0L);
            System.GC.Collect();

            watch.Restart();
            for (int i = 0; i < objCount; i++)
            {
                object toSerialize = pubObjs[i];
                formatter.Serialize(stream, toSerialize);
            }

            watch.Stop();

            pubObjSerializeTime = watch.ElapsedMilliseconds;

            pubObjLength = stream.Length;
            stream.Position = 0;
            watch.Restart();

            for (int i = 0; i < objCount; i++)
            {
                GenericSerializable obj = (GenericSerializable)formatter.Deserialize(stream);
            }
            watch.Stop();
            pubObjDeserializeTime = watch.ElapsedMilliseconds;

            #endregion

            stream.Close();

            lock (critical)
            {
                completed = true;
            }
        }

        public bool IsComplete()
        {
            lock (critical)
            {
                return completed;
            }
        }
    }
}
